package indzcraft.calc;

import java.util.HashMap;

public class Calc {
	
	public Stats stats;
	public Weapon mh;
	public Weapon oh;
	public Glyphs glyphs;
	public Race race;
	public GearBuffs gearbuffs;
	
	final double FIGHT_LENGTH = 300;
	
	public double dps1;
	public double total_hits = 0.;
	HashMap<String,Double> dps = new HashMap<String,Double>();
	
	public Calc(Stats stats,Weapon mh,Weapon oh,Glyphs glyphs,Race race,GearBuffs gearbuffs){
		this.stats = stats;
		this.mh = mh;
		this.oh = oh;
		this.glyphs = glyphs;
		this.race = race;
		this.gearbuffs = gearbuffs;
	}

	public void breakdown()
	{
		int max_length = 0;
		for(String name: dps.keySet()) {
			if (name.length() > max_length)
				max_length = name.length();
		}
		for(String name: dps.keySet()) {
			System.out.print(name);
			double ll = max_length - name.length() + 6;
			while(ll > 0) {
				System.out.print(" "); ll--;
			}
			double perc = ((double)Math.round(dps.get(name)/this.dps1*10000))/100;
			System.out.print(dps.get(name) + "   " + perc + "%");
			System.out.println("");
		}
	}
	
	/*public void universal_procs(){
		// universal procs
		String[] gearbuffs = this.gearbuffs.getGearBuffs();
		for(int i = 0;i < gearbuffs.length;i++){ // run through all gearbuffs
			if(this.procDB.has(gearbuffs[i])){
				ProcDB.Proc p = this.procDB.get(gearbuffs[i]);
				System.out.println(gearbuffs[i] + " " + p.toString());
				
				double boost = p.boost(total_hits);
				if(p.stat.equals("haste"))
					this.stats.procs_haste += boost;
				else if(p.stat.equals("agi"))
					this.stats.procs_agi += boost;
				else if(p.stat.equals("ap"))
					this.stats.procs_ap += boost;
			}	
		}
	}*/
	
	public double get_uptime(double proc_chance, int max_stacks,int duration){
		if(proc_chance >= 1)
			return max_stacks;
		double q = 1 - proc_chance * total_hits;
		double Q = Math.pow(q,duration);
		double P = 1 - Q;
		double uptime = P * (1 - Math.pow(P,max_stacks)) / Q;
		return uptime;
	}
	
	public double get_uptime_with_rampup(double proc_chance, int max_stacks,int duration){
		double time_to_max = max_stacks/(proc_chance*total_hits);
		double fight_dur = FIGHT_LENGTH;
		double uptime = (fight_dur-time_to_max)*max_stacks + time_to_max*max_stacks/2;
		uptime /= fight_dur;
		return uptime;
	}
	
	public double get_uptime_with_rampup_dagger3(double proc_chance, int max_stacks,int duration){
		double time_to_max = max_stacks/(proc_chance*total_hits);
		double fight_dur = FIGHT_LENGTH;
		//System.out.println("time_to_max: " +time_to_max);
		double rest = fight_dur % time_to_max;
		int tomax = (int) Math.round(((fight_dur - rest) / time_to_max));
		//System.out.println("tomax " + tomax);
		//System.out.println("rest: " + rest);
		double rest_max_stacks = tomax*(proc_chance*total_hits);
		//double uptime = tomax*time_to_max*max_stacks + rest*rest_max_stacks*max_stacks;
		//uptime /= fight_dur;
		
		double avg_stacks_to_max_stacks = max_stacks/2;
		double avg_stacks_to_max = rest_max_stacks*max_stacks/2;
		
		double uptime = tomax * avg_stacks_to_max_stacks + rest_max_stacks * avg_stacks_to_max;
		uptime /= (tomax+rest_max_stacks);
		
		return uptime;
	}
	
	public double get_ss_dmg()
	{
		double[] ranks_aggression = new double[]{0.0,0.07,0.14,0.20};
		double aggression = ranks_aggression[this.stats.talents.get("aggression")];
		double damage = this.mh.speed * this.mh.dps + this.mh.get_normalized_speed() * this.stats.get_ap() / 14.;
		double mod = 1 + 0.1 * this.stats.talents.get("imp_sinister_strike") + aggression;
		return (damage + 200) * mod  * (1 + gearbuffs.get("dagger_combat_buff") * 0.45);
	}
	
	public double get_rvs_dmg()
	{
		double damage = this.mh.speed * (this.mh.dps + this.stats.get_ap() / 14.);
		double mod = 1.25;
		return damage * mod * (1 + gearbuffs.get("dagger_combat_buff") * 0.45);
	}
	
	public double get_mg_dmg()
	{
		double damage = this.mh.speed * this.mh.dps + this.mh.get_normalized_speed() * this.stats.get_ap() / 14;
		return damage;
	}
	
	public double get_ks_mh_dmg()
	{
		double damage = this.mh.speed * this.mh.dps + this.mh.get_normalized_speed() * this.stats.get_ap() / 14;
		double mod = 1.2 + 0.1 * this.glyphs.killing_spree;
		return damage * mod;
	}
	
	public double get_ks_oh_dmg()
	{
		double damage = this.oh.speed * this.oh.dps + this.oh.get_normalized_speed() * this.stats.get_ap() / 14;
		double mod = 0.875 * (1.2 + 0.1 * this.glyphs.killing_spree);
		return damage * mod;
	}
	
	public double get_ip_dmg()
	{
		double damage = 352 + 0.09 * this.stats.get_ap();
		return damage;
	}
	
	public double get_dp_dmg(int stacks)
	{
		double damage = (135 * 4 + 0.14 * this.stats.get_ap()) * stacks / 4;
		return damage;
	}
	
	public double get_mh_dmg()
	{
		double damage = this.mh.speed * (this.mh.dps + this.stats.get_ap() / 14.);
		return damage;
	}
	
	public double get_oh_dmg()
	{
		double damage = this.oh.speed * (this.oh.dps + this.stats.get_ap() / 14.);
		double mod = 0.5;
		if(this.stats.talents.isCombat())
			mod = 0.875;
		return damage * mod;
	}

}
